package com.zndroid.base.ui.common;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.res.ResourcesCompat;

import com.zndroid.base.R;
import com.zndroid.base.callback.inner.mvp.IView;
import com.zndroid.base.annotation.ISplashTimerType;
import com.zndroid.base.databinding.ZBaseActLayoutCommonSplashBinding;
import com.zndroid.base.exception.SupplyIsUnsupportedOperationException;
import com.zndroid.base.ui.impl.BaseActivity;
import com.zndroid.base.ui.impl.BasePresenter;
import com.zndroid.base.widgets.CountDownTextView;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * like this
 * <code>
 *     <activity
 *             android:name="com.zndroid.base.ui.common.CommonSplashActivity"
 *             android:noHistory="false"
 *             android:theme="@style/Base.Theme.Splash"
 *             android:taskAffinity=":base_splash"
 *             >
 *             <intent-filter>
 *                 <action android:name="android.intent.action.MAIN" />
 *
 *                 <category android:name="android.intent.category.LAUNCHER" />
 *             </intent-filter>
 *         </activity>
 * </code>
 * @author lazy
 * @date 3/3/21
 */
@SuppressWarnings("unused")
public abstract class CommonSplashActivity<P extends BasePresenter<? extends IView>> extends BaseActivity<ZBaseActLayoutCommonSplashBinding, P> {
    private final long DEF_DURATION = 5_000;
    private final long DEF_INTERVAL = 1_000;
    private final int OFFSET = 200;

    private long mRemainingTime;
    private CountDownLatch mCountDownLatch;

    /** Internal weight */
    private TextView textView;
    private CountDownTimer countDownTimer;
    private CountDownTextView countDownTextView;

    private ViewGroup rootView;
    private View supplyView;
    private Intent mainIntent;

    /** bug fixed by lazy, click skip and count down finish is asynchronously */
    private final AtomicBoolean isAlreadyJump = new AtomicBoolean(false);

    private final AtomicBoolean isInterceptJump = new AtomicBoolean(false);

    /**
     * wait notify goto main page
     * @return true or false
     * */
    protected abstract boolean isSupplyWaitNotify();

    /**
     * supply Intent for jump when countdown over or click ‘jump’
     * @return Intent
     * */
    protected abstract Intent onSupplyMainIntent();

    /**
     * supply custom view
     * */
    protected View onSupplyView() {
        return null;
    }

    /**
     * 是否是第一次启动，等等...逻辑细节，子类复写实现 <br>
     * logic for skip splash to main page, child can override it.
     * @return true or false
     * */
    protected abstract boolean isSupplySkipSplash();

    /**
     * 给开发者更多的创意空间
     *
     * @param textView {@link TextView} or {@link CountDownTextView}
     * @param duration duration
     * */
    protected abstract void onUpdateText(TextView textView, long duration);

    /**
     * 是否拦截跳转，可以用于子类实现其他逻辑，默认：不拦截
     * <br>
     * See ALso: {@link #toMainPage(boolean)}
     *
     * @param intercept true will be not jump when time's up
     * */
    protected void toInterceptJump(boolean intercept) {
        if (intercept) {
            //如果被拦截，则释放已初始化的资源
            release();
        }
        isInterceptJump.set(intercept);
    }

    protected void notifyToMain() {
        if (isSupplyWaitNotify()) {
            logD("all background work done...");

            toMainPage(false);
        } else {
            logW("UnSupported call this if 'isSupplyWaitNotify' = false");
        }
    }

    protected void toRestartSplash() {
        toInterceptJump(false);
        startSplash();
    }

    @Override
    protected boolean isSupportNetworkMonitor() {
        //闪屏页不需要自动进行网络监听
        return false;
    }

    @Override
    protected ZBaseActLayoutCommonSplashBinding onSupplyViewBinding(@NonNull LayoutInflater layoutInflater) {
        //layout is:
        //R.layout.z_base_act_layout_common_splash
        return ZBaseActLayoutCommonSplashBinding.inflate(layoutInflater);
    }

    @Override
    protected void doOnCreate(@Nullable Bundle savedInstanceState) {
        super.doOnCreate(savedInstanceState);

        //init start
        mCountDownLatch = new CountDownLatch(isSupplyWaitNotify() ? 2 : 1);
        mainIntent = onSupplyMainIntent();

        if (onSupplyDuration() > 0) {
            mRemainingTime = onSupplyDuration();
        }

        appendView();
        //init end

        if (isSupplySkipSplash()) {
            if (isSupplyWaitNotify()) {
                throw new SupplyIsUnsupportedOperationException("#isSupplySkipSplash with #isSupplyWaitNotify", "something logic at splashing, but U skip it already.");
            } else {
                toMainPage(false);
            }
        } else {
            if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) > 0) {
                //为了防止重复启动多个闪屏页面，解决通过安装器安装后打开
                finishAffinity();
                return;
            }

            startSplash();
        }
    }

    private void appendView() {
        //findViewById(R.id.base_fl_contain)
        FrameLayout containFrameLayout = currentViewBinding().baseFlContain;
        rootView = containFrameLayout;
        rootView.requestFocus();

        supplyView = onSupplyView();

        if (null != supplyView) {
            containFrameLayout.addView(supplyView);
        }
    }

    private void startSplashByNone(long duration) {
        countDownTimer = new CountDownTimer(duration, DEF_INTERVAL) {
            @SuppressLint("SetTextI18n")
            @Override
            public void onTick(long millisUntilFinished) {
                mRemainingTime = millisUntilFinished;
            }

            @Override
            public void onFinish() {
                logD("countdown time over...");

                toMainPage(false);
                cancel();
            }
        };
        countDownTimer.start();
    }

    private void startSplashByCapsule(long duration) {
        //findViewById(R.id.base_tv_countdown_time_type_def)
        textView = currentViewBinding().baseTvCountdownTimeTypeDef;

        textView.setVisibility(View.VISIBLE);
        textView.setBackgroundResource(R.drawable.z_base_shape_splash_timer_capsule_bg);
        textView.setOnClickListener(v -> {
            if (!isSupplyWaitNotify()) {
                if (null != countDownTimer) {
                    countDownTimer.cancel();
                }
                toMainPage(true);
            } else {
                logW("skip clicked invalid, waiting for notification");
            }
        });

        countDownTimer = new CountDownTimer(duration, DEF_INTERVAL) {
            @Override
            public void onTick(long millisUntilFinished) {
                mRemainingTime = millisUntilFinished;
                onUpdateText(textView, millisUntilFinished / DEF_INTERVAL);
            }

            @Override
            public void onFinish() {
                logD("countdown time over...");

                toMainPage(false);
                cancel();
            }
        };
        countDownTimer.start();
    }

    private void startSplashByCircle(long duration) {
        //findViewById(R.id.base_tv_countdown_time_type_circle)
        countDownTextView = currentViewBinding().baseTvCountdownTimeTypeCircle;

        countDownTextView.setVisibility(View.VISIBLE);
        countDownTextView.setOnClickListener(v -> {
            if (!isSupplyWaitNotify()) {
                if (null != countDownTextView.getAnimator()) {
                    countDownTextView.getAnimator().cancel();
                }

                toMainPage(true);
            } else {
                logW("skip clicked invalid, waiting for notification");
            }
        });
        countDownTextView.setCallBack(() -> toMainPage(false));
        onUpdateText(countDownTextView, 0);
        countDownTextView.start(duration);
    }

    private void startSplashByRoundRect(long duration) {
        //findViewById(R.id.base_tv_countdown_time_type_def)
        textView = currentViewBinding().baseTvCountdownTimeTypeDef;

        textView.setVisibility(View.VISIBLE);
        textView.setBackgroundResource(R.drawable.z_base_shape_splash_timer_round_rect_bg);
        textView.setOnClickListener(v -> {
            if (!isSupplyWaitNotify()) {
                if (null != countDownTimer) {
                    countDownTimer.cancel();
                }
                toMainPage(true);
            } else {
                logW("skip clicked invalid, waiting for notification");
            }
        });

        countDownTimer = new CountDownTimer(duration, DEF_INTERVAL) {
            @Override
            public void onTick(long millisUntilFinished) {
                mRemainingTime = millisUntilFinished;
                onUpdateText(textView, millisUntilFinished / DEF_INTERVAL);
            }

            @Override
            public void onFinish() {
                logD("countdown time over...");

                toMainPage(false);
                cancel();
            }
        };
        countDownTimer.start();
    }

    private void startSplash() {
        mRemainingTime += OFFSET;

        switch (onSupplyTimerType()) {
            case ISplashTimerType.TYPE_CAPSULE:
                startSplashByCapsule(mRemainingTime);
                break;
            case ISplashTimerType.TYPE_CIRCLE:
                startSplashByCircle(mRemainingTime);
                break;
            case ISplashTimerType.TYPE_ROUND_RECT:
                startSplashByRoundRect(mRemainingTime);
                break;
            case ISplashTimerType.TYPE_NONE:
            default:
                startSplashByNone(mRemainingTime);
                break;
        }

        doSplash();
    }

    @Override
    protected void doOnDestroy() {
        release();
    }

    private void release() {
        if (null != countDownTimer) {
            countDownTimer.cancel();
            countDownTimer = null;
        }

        if (null != countDownTextView && countDownTextView.getAnimator().isRunning()) {
            countDownTextView.getAnimator().cancel();
            countDownTextView.clearAnimation();
        }
    }

    /**
     * you can do something when splashing by Override it
     * but, not allow consume a lot of time operation at here. time < {@link #onSupplyDuration()}
     * */
    protected void doSplash() {}

    /**
     * you can do something when splash completed by Override it
     * */
    protected void didSplash() {}

    /**
     * splash duration, units：millisecond
     * */
    protected long onSupplyDuration() {
        return DEF_DURATION;
    }

    protected @ISplashTimerType
    int onSupplyTimerType() {
        return ISplashTimerType.TYPE_NONE;
    }

    /*每当条件满足时，尝试进入主界面*/
    private void toMainPage(boolean skipClicked) {
        mCountDownLatch.countDown();

        if (skipClicked) {
            logD("skip clicked" + (mCountDownLatch.getCount() == 0 ? "" : " and waiting notify to main, because of #isSupplyWaitNotify() is true."));
        }

        if (null != mainIntent && !isAlreadyJump.get() && !isInterceptJump.get()) {
            if (mCountDownLatch.getCount() == 0) {/*跳转条件全部满足*/
                isAlreadyJump.set(true);
                startActivity(mainIntent);
                didSplash();
                this.finishAffinity();
            }
        } else {
            logW("to main condition not met ...?\n" +
                    " count=" + mCountDownLatch.getCount() + "\n" +
                    " intent=" + mainIntent + "\n" +
                    " isAlreadyJump=" + isAlreadyJump.get() + "\n" +
                    " isInterceptJump=" + isInterceptJump.get());
        }
    }

    @Override
    protected int onSupplyStatusBarColor() {
        return ResourcesCompat.getColor(getResources(), android.R.color.white, getTheme());
    }

    /**
     * Not Recommended, defined 'style' on your 'values'
     * and use like this:
     * <code>
     *     android:theme="@style/Base.Theme.Splash"
     * </code>
     * */
    @Deprecated
    @Override
    protected boolean isFullScreen() {
        //use 'style' impl
        return false;
    }

    /**
     * can back or not on pressed back, default true means can back.
     * */
    protected boolean isCanBackOnRunning() {
        return true;
    }

    @Override
    public void onBackPressed() {
        if (isCanBackOnRunning()) {
            gotoLauncher();
        } else {
            //nothing and ignore
            logW("not support back, because of override '#isCanBackOnRunning()' is false.");
        }
    }

    private void gotoLauncher() {
        Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
        launcherIntent.addCategory(Intent.CATEGORY_HOME);
        startActivity(launcherIntent);
    }
}
